home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / deluge / common.py < prev    next >
Text File  |  2009-06-16  |  12KB  |  493 lines

  1. #
  2. # common.py
  3. #
  4. # Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
  5. #
  6. # Deluge is free software.
  7. #
  8. # You may redistribute it and/or modify it under the terms of the
  9. # GNU General Public License, as published by the Free Software
  10. # Foundation; either version 3 of the License, or (at your option)
  11. # any later version.
  12. #
  13. # deluge is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. # See the GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with deluge.    If not, write to:
  20. #     The Free Software Foundation, Inc.,
  21. #     51 Franklin Street, Fifth Floor
  22. #     Boston, MA  02110-1301, USA.
  23. #
  24. #    In addition, as a special exception, the copyright holders give
  25. #    permission to link the code of portions of this program with the OpenSSL
  26. #    library.
  27. #    You must obey the GNU General Public License in all respects for all of
  28. #    the code used other than OpenSSL. If you modify file(s) with this
  29. #    exception, you may extend this exception to your version of the file(s),
  30. #    but you are not obligated to do so. If you do not wish to do so, delete
  31. #    this exception statement from your version. If you delete this exception
  32. #    statement from all source files in the program, then also delete it here.
  33. #
  34.  
  35. #
  36.  
  37.  
  38. """Common functions for various parts of Deluge to use."""
  39.  
  40. import os
  41. import time
  42. import subprocess
  43. import platform
  44.  
  45. import pkg_resources
  46. import xdg, xdg.BaseDirectory
  47.  
  48. LT_TORRENT_STATE = {
  49.     "Queued": 0,
  50.     "Checking": 1,
  51.     "Downloading Metadata": 2,
  52.     "Downloading": 3,
  53.     "Finished": 4,
  54.     "Seeding": 5,
  55.     "Allocating": 6,
  56.     "Checking Resume Data": 7,
  57.     0: "Queued",
  58.     1: "Checking",
  59.     2: "Downloading Metadata",
  60.     3: "Downloading",
  61.     4: "Finished",
  62.     5: "Seeding",
  63.     6: "Allocating",
  64.     7: "Checking Resume Data"
  65. }
  66.  
  67. TORRENT_STATE = [
  68.     "Allocating",
  69.     "Checking",
  70.     "Downloading",
  71.     "Seeding",
  72.     "Paused",
  73.     "Error",
  74.     "Queued"
  75. ]
  76.  
  77. FILE_PRIORITY = {
  78.     0: "Do Not Download",
  79.     1: "Normal Priority",
  80.     2: "High Priority",
  81.     5: "Highest Priority",
  82.     "Do Not Download": 0,
  83.     "Normal Priority": 1,
  84.     "High Priority": 2,
  85.     "Highest Priority": 5
  86. }
  87.  
  88. def get_version():
  89.     """
  90.     Returns the program version from the egg metadata
  91.  
  92.     :returns: the version of Deluge
  93.     :rtype: string
  94.  
  95.     """
  96.     return pkg_resources.require("Deluge")[0].version
  97.  
  98. def get_revision():
  99.     """
  100.     The svn revision of the build if available
  101.  
  102.     :returns: the svn revision, or ""
  103.     :rtype: string
  104.  
  105.     """
  106.     revision = ""
  107.     try:
  108.         f = open(pkg_resources.resource_filename("deluge", os.path.join("data", "revision")))
  109.         revision = f.read()
  110.         f.close()
  111.     except IOError, e:
  112.         pass
  113.  
  114.     return revision
  115.  
  116. def get_default_config_dir(filename=None):
  117.     """
  118.     :param filename: if None, only the config path is returned, if provided, a path including the filename will be returned
  119.     :returns: a file path to the config directory and optional filename
  120.     :rtype: string
  121.  
  122.     """
  123.     if windows_check():
  124.         if filename:
  125.             return os.path.join(os.environ.get("APPDATA"), "deluge", filename)
  126.         else:
  127.             return os.path.join(os.environ.get("APPDATA"), "deluge")
  128.     else:
  129.         if filename:
  130.             return os.path.join(xdg.BaseDirectory.save_config_path("deluge"), filename)
  131.         else:
  132.             return xdg.BaseDirectory.save_config_path("deluge")
  133.  
  134. def get_default_download_dir():
  135.     """
  136.     :returns: the default download directory
  137.     :rtype: string
  138.  
  139.     """
  140.     if windows_check():
  141.         return os.path.expanduser("~")
  142.     else:
  143.         return os.environ.get("HOME")
  144.  
  145. def windows_check():
  146.     """
  147.     Checks if the current platform is Windows
  148.  
  149.     :returns: True or False
  150.     :rtype: bool
  151.  
  152.     """
  153.     return platform.system() in ('Windows', 'Microsoft')
  154.  
  155. def vista_check():
  156.     """
  157.     Checks if the current platform is Windows Vista
  158.  
  159.     :returns: True or False
  160.     :rtype: bool
  161.  
  162.     """
  163.     return platform.release() == "Vista"
  164.  
  165. def osx_check():
  166.     """
  167.     Checks if the current platform is Mac OS X
  168.  
  169.     :returns: True or False
  170.     :rtype: bool
  171.  
  172.     """
  173.     return platform.system() == "Darwin"
  174.  
  175. def get_pixmap(fname):
  176.     """
  177.     Provides easy access to files in the deluge/data/pixmaps folder within the Deluge egg
  178.  
  179.     :param fname: the filename to look for
  180.     :returns: a path to a pixmap file included with Deluge
  181.     :rtype: string
  182.  
  183.     """
  184.     return pkg_resources.resource_filename("deluge", os.path.join("data", \
  185.                                            "pixmaps", fname))
  186.  
  187. def open_file(path):
  188.     """
  189.     Opens a file or folder using the system configured program
  190.  
  191.     :param path: the path to the file or folder to open
  192.  
  193.     """
  194.     if windows_check():
  195.         os.startfile("%s" % path)
  196.     else:
  197.         subprocess.Popen(["xdg-open", "%s" % path])
  198.  
  199. def open_url_in_browser(url):
  200.     """
  201.     Opens a url in the desktop's default browser
  202.  
  203.     :param url: the url to open
  204.     """
  205.     import threading
  206.     import webbrowser
  207.     class BrowserThread(threading.Thread):
  208.         def __init__(self, url):
  209.             threading.Thread.__init__(self)
  210.             self.url = url
  211.         def run(self):
  212.             webbrowser.open(self.url)
  213.     BrowserThread(url).start()
  214.  
  215. ## Formatting text functions
  216.  
  217. def fsize(fsize_b):
  218.     """
  219.     Formats the bytes value into a string with KiB, MiB or GiB units
  220.  
  221.     :param fsize_b: int, the filesize in bytes
  222.     :returns: formatted string in KiB, MiB or GiB units
  223.     :rtype: string
  224.  
  225.     **Usage**
  226.  
  227.     >>> fsize(112245)
  228.     '109.6 KiB'
  229.  
  230.     """
  231.     fsize_kb = fsize_b / 1024.0
  232.     if fsize_kb < 1024:
  233.         return "%.1f KiB" % fsize_kb
  234.     fsize_mb = fsize_kb / 1024.0
  235.     if fsize_mb < 1024:
  236.         return "%.1f MiB" % fsize_mb
  237.     fsize_gb = fsize_mb / 1024.0
  238.     return "%.1f GiB" % fsize_gb
  239.  
  240. def fpcnt(dec):
  241.     """
  242.     Formats a string to display a percentage with two decimal places
  243.  
  244.     :param dec: float, the ratio in the range [0.0, 1.0]
  245.     :returns: a formatted string representing a percentage
  246.     :rtype: string
  247.  
  248.     **Usage**
  249.  
  250.     >>> fpcnt(0.9311)
  251.     '93.11%'
  252.  
  253.     """
  254.     return '%.2f%%' % (dec * 100)
  255.  
  256. def fspeed(bps):
  257.     """
  258.     Formats a string to display a transfer speed utilizing :func:`fsize`
  259.  
  260.     :param bps: int, bytes per second
  261.     :returns: a formatted string representing transfer speed
  262.     :rtype: string
  263.  
  264.     **Usage**
  265.  
  266.     >>> fspeed(43134)
  267.     '42.1 KiB/s'
  268.  
  269.     """
  270.     return '%s/s' % (fsize(bps))
  271.  
  272. def fpeer(num_peers, total_peers):
  273.     """
  274.     Formats a string to show 'num_peers' ('total_peers')
  275.  
  276.     :param num_peers: int, the number of connected peers
  277.     :param total_peers: int, the total number of peers
  278.     :returns: a formatted string: num_peers (total_peers), if total_peers < 0, then it will not be shown
  279.     :rtype: string
  280.  
  281.     **Usage**
  282.  
  283.     >>> fpeer(10, 20)
  284.     '10 (20)'
  285.     >>> fpeer(10, -1)
  286.     '10'
  287.  
  288.     """
  289.     if total_peers > -1:
  290.         return "%d (%d)" % (num_peers, total_peers)
  291.     else:
  292.         return "%d" % num_peers
  293.  
  294. def ftime(seconds):
  295.     """
  296.     Formats a string to show time in a human readable form
  297.  
  298.     :param seconds: int, the number of seconds
  299.     :returns: a formatted time string, will return '' if seconds == 0
  300.     :rtype: string
  301.  
  302.     **Usage**
  303.  
  304.     >>> ftime(23011)
  305.     '6h 23m'
  306.  
  307.     """
  308.     if seconds == 0:
  309.         return ""
  310.     if seconds < 60:
  311.         return '%ds' % (seconds)
  312.     minutes = seconds / 60
  313.     if minutes < 60:
  314.         seconds = seconds % 60
  315.         return '%dm %ds' % (minutes, seconds)
  316.     hours = minutes / 60
  317.     if hours < 24:
  318.         minutes = minutes % 60
  319.         return '%dh %dm' % (hours, minutes)
  320.     days = hours / 24
  321.     if days < 7:
  322.         hours = hours % 24
  323.         return '%dd %dh' % (days, hours)
  324.     weeks = days / 7
  325.     if weeks < 52:
  326.         days = days % 7
  327.         return '%dw %dd' % (weeks, days)
  328.     years = weeks / 52
  329.     weeks = weeks % 52
  330.     return '%dy %dw' % (years, weeks)
  331.  
  332. def fdate(seconds):
  333.     """
  334.     Formats a date string in the locale's date representation based on the systems timezone
  335.  
  336.     :param seconds: float, time in seconds since the Epoch
  337.     :returns: a string in the locale's date representation or "" if seconds < 0
  338.     :rtype: string
  339.  
  340.     """
  341.     if seconds < 0:
  342.         return ""
  343.     return time.strftime("%x", time.localtime(seconds))
  344.  
  345. def is_url(url):
  346.     """
  347.     A simple regex test to check if the URL is valid
  348.  
  349.     :param url: string, the url to test
  350.     :returns: True or False
  351.     :rtype: bool
  352.  
  353.     **Usage**
  354.  
  355.     >>> is_url("http://deluge-torrent.org")
  356.     True
  357.  
  358.     """
  359.     import re
  360.     return bool(re.search('^(https?|ftp|udp)://', url))
  361.  
  362. def is_magnet(uri):
  363.     """
  364.     A check to determine if a uri is a valid bittorrent magnet uri
  365.  
  366.     :param uri: string, the uri to check
  367.     :returns: True or False
  368.     :rtype: bool
  369.  
  370.     **Usage**
  371.  
  372.     >>> is_magnet("magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN")
  373.     True
  374.  
  375.     """
  376.     if uri[:20] == "magnet:?xt=urn:btih:":
  377.         return True
  378.     return False
  379.  
  380. def fetch_url(url):
  381.     """
  382.     Downloads a torrent file from a given URL and checks the file's validity
  383.  
  384.     :param url: string, the url of the .torrent file to fetch
  385.     :returns: the filepath to the downloaded file
  386.     :rtype: string
  387.  
  388.     """
  389.     import urllib
  390.     from deluge.log import LOG as log
  391.     try:
  392.         filename, headers = urllib.urlretrieve(url)
  393.     except IOError:
  394.         log.debug("Network error while trying to fetch torrent from %s", url)
  395.     else:
  396.         if filename.endswith(".torrent") or headers["content-type"] ==\
  397.         "application/x-bittorrent":
  398.             return filename
  399.         else:
  400.             log.debug("URL doesn't appear to be a valid torrent file: %s", url)
  401.             return None
  402.  
  403. def create_magnet_uri(infohash, name=None, trackers=[]):
  404.     """
  405.     Creates a magnet uri
  406.  
  407.     :param infohash: string, the info-hash of the torrent
  408.     :param name: string, the name of the torrent (optional)
  409.     :param trackers: list of strings, the trackers to announce to (optional)
  410.  
  411.     :returns: a magnet uri string
  412.     :rtype: string
  413.  
  414.     """
  415.     from base64 import b32encode
  416.     uri = "magnet:?xt=urn:btih:" + b32encode(infohash.decode("hex"))
  417.     if name:
  418.         uri = uri + "&dn=" + name
  419.     if trackers:
  420.         for t in trackers:
  421.             uri = uri + "&tr=" + t
  422.  
  423.     return uri
  424.  
  425. def get_path_size(path):
  426.     """
  427.     Gets the size in bytes of 'path'
  428.  
  429.     :param path: string, the path to check for size
  430.     :returns: the size in bytes of the path or -1 if the path does not exist
  431.     :rtype: int
  432.  
  433.     """
  434.     if not os.path.exists(path):
  435.         return -1
  436.  
  437.     if os.path.isfile(path):
  438.         return os.path.getsize(path)
  439.  
  440.     dir_size = 0
  441.     for (p, dirs, files) in os.walk(path):
  442.         for file in files:
  443.             filename = os.path.join(p, file)
  444.             dir_size += os.path.getsize(filename)
  445.     return dir_size
  446.  
  447. def free_space(path):
  448.     """
  449.     Gets the free space available at 'path'
  450.  
  451.     :param path: string, the path to check
  452.     :returns: the free space at path in bytes
  453.     :rtype: int
  454.  
  455.     """
  456.     if windows_check():
  457.         import win32file
  458.         sectors, bytes, free, total = map(long, win32file.GetDiskFreeSpace(path))
  459.         return (free * sectors * bytes)
  460.     else:
  461.         disk_data = os.statvfs(path)
  462.         block_size = disk_data.f_bsize
  463.         return disk_data.f_bavail * block_size
  464.  
  465. def is_ip(ip):
  466.     """
  467.     A simple test to see if 'ip' is valid
  468.  
  469.     :param ip: string, the ip to check
  470.     :returns: True or False
  471.     :rtype: bool
  472.  
  473.     ** Usage **
  474.  
  475.     >>> is_ip("127.0.0.1")
  476.     True
  477.  
  478.     """
  479.     import socket
  480.     #first we test ipv4
  481.     try:
  482.         if socket.inet_pton(socket.AF_INET, "%s" % (ip)):
  483.             return True
  484.     except socket.error:
  485.         if not socket.has_ipv6:
  486.             return False
  487.     #now test ipv6
  488.     try:
  489.         if socket.inet_pton(socket.AF_INET6, "%s" % (ip)):
  490.             return True
  491.     except socket.error:
  492.         return False
  493.